﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using JRFinances.Models;
using JRFinances.ViewModels;
using AutoMapper;
using JRFinances.ViewModels.RealTransactions;
using JRFinances.ViewModels.Common;
using System.Data.Objects;
using PagedList;

namespace JRFinances.Controllers
{
    [Authorize]
    public class RealTransactionsController : Controller
    {
        private JRFinancesEntities db = new JRFinancesEntities();

        public ViewResult ShowRealTransactionsHistory(string Company, string Buy, string Sell, string DateFrom, string DateTo, int? page)
        {
            var realtransactions = db.RealTransactions.Include("Company").Include("User").Where(n => n.User.Name == User.Identity.Name);

            if (!string.IsNullOrEmpty(Company))
            {
                realtransactions = realtransactions.Where(n => n.Company.Name == Company);
            }
            if (!string.IsNullOrEmpty(DateFrom))
            {
                DateTime dateTimeDate;
                if (DateTime.TryParse(DateFrom, out dateTimeDate))
                {
                    realtransactions = realtransactions.Where(n => EntityFunctions.TruncateTime(n.Date) >= dateTimeDate.Date); // dodanie filtrowania po dacie jesli wystapil taki parametr
                }
            }
            if (!string.IsNullOrEmpty(DateTo))
            {
                DateTime dateTimeDate;
                if (DateTime.TryParse(DateTo, out dateTimeDate))
                {
                    realtransactions = realtransactions.Where(n => EntityFunctions.TruncateTime(n.Date) <= dateTimeDate.Date); // dodanie filtrowania po dacie jesli wystapil taki parametr
                }
            }
            if (Buy == "on" && string.IsNullOrEmpty(Sell))
            {
                realtransactions = realtransactions.Where(n => n.Buy == true);
            }
            if (Sell == "on" && string.IsNullOrEmpty(Buy))
            {
                realtransactions = realtransactions.Where(n => n.Buy == false);
            }

            var realTransactionsViewModelList = new List<RealTransactionsHistoryViewModel>();
            realtransactions.ToList().ForEach(x => realTransactionsViewModelList.Add(Mapper.Map<RealTransactionsHistoryViewModel>(x)));

            return View(realTransactionsViewModelList);
        }

        public ActionResult Create()
        {
            ViewBag.CompanyId = new SelectList(db.Companies, "CompanyId", "Name");
            var realtransaction = new CreateRealTransactionViewModel() { Date = DateTime.Today, Buy = true };
            return View(realtransaction);
        }

        [HttpPost]
        public ActionResult Create(CreateRealTransactionViewModel realTransactionViewModel)
        {
            if (ModelState.IsValid) // sprawdzenie stanu modelu
            {
                var realTransaction = Mapper.Map<RealTransaction>(realTransactionViewModel);
                realTransaction.UserId = db.Users.Single(n => n.Name == User.Identity.Name).UserId; // ustawienie id obecnego uzytkownika dla dodawanej transakcji
                db.RealTransactions.AddObject(realTransaction); // dodanie nowej transakcji do kontekstu bazy danych
                db.SaveChanges(); // zapisanie zmian na bazie danych
                return RedirectToAction("ShowCurrentlyOwnedShares");
            }
            ViewBag.CompanyId = new SelectList(db.Companies, "CompanyId", "Name", realTransactionViewModel.CompanyId);
            return View(realTransactionViewModel); // powrot do interfejsu dodawania transakcji w przypadku wystapienia bledow
        }

        public ActionResult Delete(int id)
        {
            RealTransaction realtransaction = db.RealTransactions.Single(r => r.TransactionId == id);
            db.RealTransactions.DeleteObject(realtransaction);
            db.SaveChanges();
            return RedirectToAction("ShowCurrentlyOwnedShares");
        }

        public ActionResult Sell(string companyName)
        {
            var sharesBought = from n in db.RealTransactions
                               where n.User.Name == User.Identity.Name && n.Buy == true && n.Company.Name == companyName
                               group n by n.Company.Name into g
                               select new { Name = g.Key, VolumeBought = g.Sum(p => p.Volume) };

            var sharesSold = from n in db.RealTransactions
                             where n.User.Name == User.Identity.Name && n.Buy == false && n.Company.Name == companyName
                             group n by n.Company.Name into g
                             select new { Name = g.Key, VolumeSold = g.Sum(p => p.Volume) };

            var shares = from b in sharesBought
                         join s in sharesSold on b.Name equals s.Name into ps
                         from s in ps.DefaultIfEmpty()
                         select new { Name = b.Name, VolumeLeft = s.VolumeSold == null ? b.VolumeBought : b.VolumeBought - s.VolumeSold };

            var shareForSale = shares.Single();

            double currentPrice = db.Quotations.Where(n => n.Company.Name == shareForSale.Name).OrderByDescending(n => n.Date).First().Price;

            var sellRealTransactionViewModel = new SellRealTransactionViewModel() { CompanyName = shareForSale.Name, MaxVolume = shareForSale.VolumeLeft, Date = DateTime.Now, Price = currentPrice };

            return View(sellRealTransactionViewModel);
        }

        [HttpPost]
        public ActionResult Sell(SellRealTransactionViewModel sellRealTransactionViewModel)
        {
            if (ModelState.IsValid && sellRealTransactionViewModel.CustomValidation())
            {
                var realTransaction = Mapper.Map<RealTransaction>(sellRealTransactionViewModel);
                realTransaction.UserId = db.Users.Single(n => n.Name == User.Identity.Name).UserId;
                realTransaction.CompanyId = db.Companies.Single(n => n.Name == sellRealTransactionViewModel.CompanyName).CompanyId;
                db.RealTransactions.AddObject(realTransaction);
                db.SaveChanges();
                return RedirectToAction("ShowCurrentlyOwnedShares");
            }

            ModelState.AddModelError("", "Blad skladania zlecenia sprzedazy.");
            return View(sellRealTransactionViewModel);
        }

        public ActionResult ShowCurrentlyOwnedShares()
        {
            var currentlyOwnedSharesViewModel = GetCurrentlyOwnedShares();

            return View(currentlyOwnedSharesViewModel);
        }

        public ActionResult ShowStatistics()
        {
            string userName = User.Identity.Name;
            var stats = new ShowStatisticsRealTransactionViewModel();
            var transactionBalances = GetTransactionsBalances(userName);

            stats.TransactionBalances = transactionBalances.ToList();

            return View(stats);
        }

        public ActionResult ShowCurrentlyOwnedSharesChart()
        {
            var currentlyOwnedShares = GetCurrentlyOwnedShares();

            var xValues = new List<string>();
            var yValues = new List<double>();

            foreach (var item in currentlyOwnedShares)
            {
                xValues.Add(item.Name);
                yValues.Add(item.AvgPrice * item.VolumeLeft);
            }

            var chartViewModel = new ChartViewModel();

            chartViewModel.XValues = xValues.ToArray();
            chartViewModel.YValues = yValues.ToArray();

            return View(chartViewModel);
        }

        public ActionResult Pit(int? year)
        {
            int yearContidtion = year ?? DateTime.Today.Year;
            DateTime beginCondition = new DateTime(yearContidtion, 1, 1);
            DateTime endCondition = new DateTime(yearContidtion, 12, 31);
            string userName = User.Identity.Name;

            var years = (from tr in db.RealTransactions
                         where tr.User.Name == userName
                         select new { Year = tr.Date.Year }).Distinct();

            ViewBag.Year = new SelectList(years, "Year", "Year");

            var pit = GetTransactionBalancesForPit(yearContidtion, beginCondition, endCondition, userName);

            var pitViewModel = new PitViewModel() { PitData = pit.ToList(), Year = yearContidtion };
            return View(pitViewModel);
        }

        public ActionResult ShowRealTransactionsInGroup(RealTransactionsInGroupViewModel realTransacionsViewModel)
        {
            string currentUser = User.Identity.Name;
            realTransacionsViewModel.Data = new List<RealTransactionsInGroupDataViewModel>();

            GetDataForRealTransactionsInGroup(realTransacionsViewModel);

            var groupsWithUser = from gr in db.Groups
                                 join ug in db.UsersGroups on gr.GroupId equals ug.GroupId
                                 where ug.User.Name == currentUser
                                 select new { GroupId = gr.GroupId, GroupName = gr.Name };

            ViewBag.GroupId = new SelectList(groupsWithUser, "GroupId", "GroupName");

            return View(realTransacionsViewModel);
        }

        public double GetPriceForCompany(int id)
        {
            double currentPrice = db.Quotations.Where(n => n.CompanyId == id).OrderByDescending(n => n.Date).First().Price;

            return currentPrice;
        }

        #region Private

        private IQueryable<PitDataViewModel> GetTransactionBalancesForPit(int yearContidtion, DateTime beginCondition, DateTime endCondition, string userName)
        {
            var transactionsBought = from tr in db.RealTransactions
                                     where tr.User.Name == userName && tr.Buy == true && tr.Date <= endCondition
                                     group tr by tr.Company.Name into grp
                                     select new { Name = grp.Key, TotalPurchaseVolume = grp.Sum(n => n.Volume), TotalPurchaseCost = grp.Sum(n => n.Price * n.Volume) };

            var transactionsSold = from tr in db.RealTransactions
                                   where tr.User.Name == userName && tr.Buy == false && tr.Date >= beginCondition && tr.Date <= endCondition
                                   group tr by tr.Company.Name into grp
                                   select new { Name = grp.Key, TotalSellVolume = grp.Sum(n => n.Volume), TotalSellCost = grp.Sum(n => n.Price * n.Volume), Year = yearContidtion };

            var transactionBalances = from bt in transactionsBought
                                      join sd in transactionsSold on bt.Name equals sd.Name
                                      select new
                                      {
                                          Name = bt.Name,
                                          FixedVolume = sd.TotalSellVolume,
                                          AvgPrice = bt.TotalPurchaseCost / bt.TotalPurchaseVolume,
                                          TotalSellCost = sd.TotalSellCost,
                                          TotalPurchaseCostOfSoldDuringYear = sd.TotalSellVolume * (bt.TotalPurchaseCost / bt.TotalPurchaseVolume),
                                          BalanceOfMatched = sd.TotalSellCost - sd.TotalSellVolume * (bt.TotalPurchaseCost / bt.TotalPurchaseVolume),
                                          Year = sd.Year
                                      };

            double taxAmout = db.AnnualDatas.Single(n => n.Year == yearContidtion).TaxAmount;

            var pit = from cmp in transactionBalances
                      group cmp by cmp.Year into grp
                      select new PitDataViewModel
                      {
                          TotalIncome = grp.Sum(n => n.TotalSellCost),
                          TotalOutcome = grp.Sum(n => n.TotalPurchaseCostOfSoldDuringYear),
                          Balance = grp.Sum(n => n.BalanceOfMatched),
                          Tax = grp.Sum(n => n.BalanceOfMatched) > 0 ? grp.Sum(n => n.BalanceOfMatched) * taxAmout : 0
                      };
            return pit;
        }

        private IQueryable<TransactionBalanceViewModel> GetTransactionsBalances(string userName)
        {
            var transactionsBought = from tr in db.RealTransactions
                                     where tr.User.Name == userName && tr.Buy == true
                                     group tr by tr.Company.Name into grp
                                     select new { Name = grp.Key, TotalPurchaseVolume = grp.Sum(n => n.Volume), TotalPurchaseCost = grp.Sum(n => n.Price * n.Volume) };

            var transactionsSold = from tr in db.RealTransactions
                                   where tr.User.Name == userName && tr.Buy == false
                                   group tr by tr.Company.Name into grp
                                   select new { Name = grp.Key, TotalSellVolume = grp.Sum(n => n.Volume), TotalSellCost = grp.Sum(n => n.Price * n.Volume) };

            var transactionBalances = from bt in transactionsBought
                                      join sd in transactionsSold on bt.Name equals sd.Name
                                      select new TransactionBalanceViewModel
                                      {
                                          Name = bt.Name,
                                          FixedVolume = sd.TotalSellVolume,
                                          LeftVolume = bt.TotalPurchaseVolume - sd.TotalSellVolume,
                                          AvgPrice = bt.TotalPurchaseCost / bt.TotalPurchaseVolume,
                                          TotalSellCost = sd.TotalSellCost,
                                          BalanceOfMatched = sd.TotalSellCost - sd.TotalSellVolume * (bt.TotalPurchaseCost / bt.TotalPurchaseVolume)
                                      };
            return transactionBalances;
        }

        private List<CurrentlyOwnedSharesViewModel> GetCurrentlyOwnedShares()
        {
            var currentlyOwnedSharesViewModel = new List<CurrentlyOwnedSharesViewModel>();

            var sharesBought = from n in db.RealTransactions
                               where n.User.Name == User.Identity.Name && n.Buy == true
                               group n by n.Company.Name into g
                               select new { Name = g.Key, VolumeBought = g.Sum(p => p.Volume), TotalPurchaseCost = g.Sum(p => p.Price) };

            var sharesSold = from n in db.RealTransactions
                             where n.User.Name == User.Identity.Name && n.Buy == false
                             group n by n.Company.Name into g
                             select new { Name = g.Key, VolumeSold = g.Sum(p => p.Volume) };

            var shares = from b in sharesBought
                         join s in sharesSold on b.Name equals s.Name into ps
                         from s in ps.DefaultIfEmpty()
                         select new { Name = b.Name, VolumeLeft = s.VolumeSold == null ? b.VolumeBought : b.VolumeBought - s.VolumeSold, AvgPrice = b.TotalPurchaseCost / b.VolumeBought };

            shares = shares.Where(n => n.VolumeLeft > 0);

            shares.ToList().ForEach(n => currentlyOwnedSharesViewModel.Add(new CurrentlyOwnedSharesViewModel() { Name = n.Name, VolumeLeft = n.VolumeLeft, AvgPrice = n.AvgPrice }));

            return currentlyOwnedSharesViewModel;
        }

        private void GetDataForRealTransactionsInGroup(RealTransactionsInGroupViewModel realTransacionsViewModel)
        {
            if (realTransacionsViewModel.GroupId > 0)
            {
                var realTransactionsDataList = new List<RealTransactionsInGroupDataViewModel>();

                var realTransactions = from tr in db.RealTransactions
                                       join usr in db.Users on tr.UserId equals usr.UserId
                                       join ug in db.UsersGroups on usr.UserId equals ug.UserId
                                       where ug.GroupId == realTransacionsViewModel.GroupId && ug.Activated == true
                                       orderby tr.Date descending
                                       select new RealTransactionsInGroupDataViewModel()
                                       {
                                           Buy = tr.Buy,
                                           Date = tr.Date,
                                           CompanyName = tr.Company.Name,
                                           Price = tr.Price,
                                           TransactionId = tr.TransactionId,
                                           User = tr.User.Name,
                                           Volume = tr.Volume
                                       };

                if (!string.IsNullOrEmpty(realTransacionsViewModel.UserCriteria))
                {
                    realTransactions = realTransactions.Where(n => n.User == realTransacionsViewModel.UserCriteria);
                }
                if (!string.IsNullOrEmpty(realTransacionsViewModel.CompanyCriteria))
                {
                    realTransactions = realTransactions.Where(n => n.CompanyName == realTransacionsViewModel.CompanyCriteria);
                }
                if (realTransacionsViewModel.DateFromCriteria != null)
                {
                    realTransactions = realTransactions.Where(n => n.Date >= realTransacionsViewModel.DateFromCriteria);
                }
                if (realTransacionsViewModel.DateToCriteria != null)
                {
                    realTransactions = realTransactions.Where(n => n.Date <= realTransacionsViewModel.DateToCriteria);
                }
                if (realTransacionsViewModel.BuyCriteria && !realTransacionsViewModel.SellCriteria)
                {
                    realTransactions = realTransactions.Where(n => n.Buy == true);
                }
                if (!realTransacionsViewModel.BuyCriteria && realTransacionsViewModel.SellCriteria)
                {
                    realTransactions = realTransactions.Where(n => n.Buy == false);
                }

                realTransactions.ToList().ForEach(n => realTransactionsDataList.Add(n));

                realTransacionsViewModel.Data = realTransactionsDataList;
            }
        }

        #endregion

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}